package yicr.untils;



import javax.swing.filechooser.FileSystemView;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 操作文件本身，包含文件的复制，列出，
 */
public class NslUtilFile {
    /**
     * 判断文件是否是bom类型及时那种编码
     * BOM是“Byte Order Mark”的缩写，用于标记文件的编码。并不是所有的文本编辑工具都能识别BOM标记.
     * 在用记事本之类的程序将文本文件保存为UTF-8格式时，记事本会 在文件头前面加上几个不可见的字符（EF BB BF），就是所谓的BOM（Byte Order Mark）。
     * FE FF    UTF16BE
     * FF FE    UTF16LE
     * EF BB BF     UTF8
     * @param srcFile
     * @return
     */
    public static NslEnumFileCharSet detectEncoding(File srcFile) {
        fileNotNullExistNotDir(srcFile);
        try (InputStream inputStream = new FileInputStream(srcFile)) {
            byte[] bom = new byte[4];
            int bytesRead = inputStream.read(bom);
            if (bytesRead >= 3 && bom[0] == (byte)0xEF && bom[1] == (byte)0xBB && bom[2] == (byte)0xBF) {
                return NslEnumFileCharSet.UTF8_BOM;
            } else if (bytesRead >= 2 && bom[0] == (byte)0xFF && bom[1] == (byte)0xFE) {
                return NslEnumFileCharSet.UTF16LE_BOM;
            } else if (bytesRead >= 2 && bom[0] == (byte)0xFE && bom[1] == (byte)0xFF) {
                return NslEnumFileCharSet.UTF16BE_BOM;
            } else if (bytesRead >= 4 && bom[0] == (byte)0x00 && bom[1] == (byte)0x00 && bom[2] == (byte)0xFE && bom[3] == (byte)0xFF) {
                return NslEnumFileCharSet.UTF32BE_BOM;
            } else if (bytesRead >= 4 && bom[0] == (byte)0xFF && bom[1] == (byte)0xFE && bom[2] == (byte)0x00 && bom[3] == (byte)0x00) {
                return NslEnumFileCharSet.UTF32LE_BOM;
            } else {
                // 默认返回UTF-8编码格式
                return NslEnumFileCharSet.OTHER;
            }
        } catch (IOException e) {
            e.printStackTrace();
            // 返回空字符串表示判断失
            throw new RuntimeException("file 读取失败");
        }
    }

    static public void fileNotNullExistNotDir(File file){
        if(file==null || !file.exists() || file.isDirectory()){
            throw new RuntimeException("file 为空 或 不存在 或 为目录");
        }
    }


    /**
     * 去除 bom file 中的 bom 标识
     * @param srcFile
     */
    public static void removeFileBom(File srcFile,File targetFile) {
        fileNotNullExistNotDir(srcFile);
        NslEnumFileCharSet charSet = detectEncoding(srcFile);
        if (charSet == NslEnumFileCharSet.OTHER) {
            throw new RuntimeException("无法判断文件类型为 bom类型。");
        }
        try {
            FileInputStream fis = new FileInputStream(srcFile);
            byte[] bytes = new byte[(int) srcFile.length()];
            fis.read(bytes);
            fis.close();
            byte[] newBytes = null;
            switch (charSet) {
                case UTF8_BOM:
                    newBytes = new byte[bytes.length - 3];
                    System.arraycopy(bytes, 3, newBytes, 0, newBytes.length);
                    break;
                case UTF16BE_BOM:
                case UTF16LE_BOM:
                    newBytes = new byte[bytes.length - 2];
                    System.arraycopy(bytes, 2, newBytes, 0, newBytes.length);
                    break;
                case UTF32BE_BOM:
                case UTF32LE_BOM:
                    newBytes = new byte[bytes.length - 4];
                    System.arraycopy(bytes, 4, newBytes, 0, newBytes.length);
                    break;
            }
            FileOutputStream fos = new FileOutputStream(targetFile);
            fos.write(newBytes);
            fos.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static byte[] fileToBytes(String filePath) throws IOException {
        return fileToBytes(new File(filePath));
    }

    /**
     * file 变为 字节数组
     * @param file
     * @return
     * @throws IOException
     */
    public static byte[] fileToBytes(File file) throws IOException {
        FileInputStream fis = null;
        ByteArrayOutputStream bos = null;
        byte[] resultBytes = null;
        try {
            fis = new FileInputStream(file);
            bos = new ByteArrayOutputStream();
            byte[] bytes = new byte[1024];
            int len;
            while((len = fis.read(bytes))!=-1){
                bos.write(bytes,0,len);
            }
            resultBytes = bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            bos.close();
            fis.close();
        }
        return  resultBytes;
    }

    /**
     * byte数组转成file文件
     * @param bytes
     */
    public static void  bytesToFile(byte [] bytes,String path) throws IOException {
        //根据路径创建对象，路径不存在会自动创建
        FileOutputStream fos=new FileOutputStream(path);
        //将bytes数组读取到文件中
        fos.write(bytes,0,bytes.length);
        fos.close();
    }

    /**
     * 递归删除指定的文件夹（包括该文件夹下的所有子文件夹和文件）.
     *
     * @param folder 要删除的文件夹
     * @return 如果文件夹删除成功，则返回true；否则返回false
     */
    public static boolean deleteFolder(File folder) {
        // 检查文件夹是否存在，如果不存在，说明删除成功；如果存在，继续删除子文件夹和文件
        if (!folder.exists()) {
            return true;
        }

        // 如果文件夹是一个文件，直接删除它
        if (folder.isFile()) {
            return folder.delete();
        }

        // 如果文件夹是一个目录，遍历其下的所有子文件夹和文件，并递归地调用删除本身的方法
        boolean result = true; // 定义布尔变量，用于判断删除是否成功
        File[] files = folder.listFiles(); // 获取文件夹中的所有文件和子文件夹
        if (files != null) {
            for (File f : files) { // 遍历每个文件或子文件夹
                if (!deleteFolder(f)) { // 对文件夹进行递归删除
                    result = false; // 如果删除失败，设置布尔变量为false
                }
            }
        }

        // 删除文件夹本身
        if (!folder.delete()) {
            result = false;
        }

        return result;
    }

    /**根据绝对路径获取文件名
     * eg.
     * 1. Y:\Download\BaiduNetdiskDownload\资料—前端HTML5+CSS3\day04\02-PPT\1.text   -> 	1.text
     * @param fileAbsPath
     * @return
     */
    public static String getFileName(String fileAbsPath){
        File file=new File(fileAbsPath);
        if(!file.isFile()){
            throw new RuntimeException(fileAbsPath +" is not file.");
        }

        return file.getName();
    }


    /** 获取指定目录下的文件List
     * @param dir abs path 0f directory
     * @return  file list
     */
    public static List<File> getFilesOfDir(File dir){
        List<File> files=new ArrayList<>();
        if(dir.isDirectory()){
            File[] items=dir.listFiles();
            for (File item : items) {
                if(item.isDirectory()){
                    continue;
                }
                files.add(item);
            }
        }
        return  files;
    }

    /**获取指定绝地路径下 的 文件数组
     * @param dirAbsPath
     * @return
     */
    public static List<File> getFilesOfDir(String  dirAbsPath){
        File dir=new File(dirAbsPath);
        return getFilesOfDir(dir);
    }

    /**获取指定目录下的文件名组成的数组
     * @param dirAbsPath
     * @return
     */
    public static  List<String> getFileNamesOfDir(String  dirAbsPath){
        File dir=new File(dirAbsPath);
        List<File> files=getFilesOfDir(dir);

        List<String> fileNamesList=new ArrayList<>();
        for(File file:files){
            fileNamesList.add(file.getName());
        }
        return fileNamesList;
    }

    /**获取指定目录下的 文件绝对路径组成的 数组
     * @param dirAbsPath
     * @return
     */
    public static List<String> getFileAbsPathsOfDir(String  dirAbsPath){
        File dir=new File(dirAbsPath);
        List<File> files=getFilesOfDir(dir);

        List<String> fileAbsPathList=new ArrayList<>();
        for (File file : files) {
            fileAbsPathList.add(file.getAbsolutePath());
        }
        return fileAbsPathList;
    }


    /**
     * 根据 相对classpath目录 获得绝对路径
     * @param relativePath 相对classpath目录,目录形式如:"config/database.prop",注意首字符不能出现"/"
     * @return
     */
    public static String getAbsPathBaseOnClassPath(String relativePath) throws URISyntaxException {
        if(relativePath==null){
            throw new RuntimeException("RelativePath is null");
        }
        URL url=NslUtilFile.class.getClassLoader().getResource(relativePath);
        return getAbsPath(url);
    }


    public static File getFileBaseOnClassPath(String relativePathBasedOnClassPath ) throws URISyntaxException {
        String absPath=getAbsPathBaseOnClassPath(relativePathBasedOnClassPath);
        return new File(absPath);
    }

    /** 根据url获取绝对目录,对于 特殊字符，比如URL会转换成%D格式， 在uri=new URI(url.toString())时，会转换回来。
     * @param url	url地址
     * @return
     */
    public static String getAbsPath(URL url) throws URISyntaxException {
        if(url==null){
            throw new RuntimeException("Url is null");
        }
        URI uri = new URI(url.toString());
        String result=uri.getPath();

        if(result!=null){
            result=result.substring(1, result.length());
        }else{
            result="";
        }
        return result;
    }

    private static String MESSAGE = "";

    public static String file2String(File file){
        StringBuffer result =new StringBuffer() ;
        try{
            BufferedReader br = new BufferedReader(new FileReader(file));//构造一个BufferedReader类来读取文件
            String s = null;
            while((s = br.readLine())!=null){//使用readLine方法，一次读一行
                result = result.append("\n" +s);
            }
            br.close();
        }catch(Exception e){
            e.printStackTrace();
        }
        return result.toString();
    }

    public static String inputStream2String(InputStream in){
        String result = "";
        try{
            BufferedReader br = new BufferedReader(new InputStreamReader(in));//构造一个BufferedReader类来读取文件
            String s = null;
            while((s = br.readLine())!=null){//使用readLine方法，一次读一行
                result = result + "\n" +s;
            }
            br.close();
        }catch(Exception e){
            e.printStackTrace();
        }
        return result;
    }

    public static boolean string2File(String string,File f){
        try {
            BufferedWriter output = new BufferedWriter(new FileWriter(f));
            output.write(string);
            output.close();
        }
        catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return false;
        }
        return true;
    }

    public static boolean string2File(String string,File f,String codeSet){
        //UTF-16LE

        try {
			/*BufferedWriter output = new BufferedWriter(new FileWriter(f));
			output.write(string);
	        output.close();*/

            OutputStreamWriter os2 = new OutputStreamWriter(new FileOutputStream(f),codeSet);
            os2.write(string);
            os2.close();
        }
        catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return false;
        }
        return true;
    }



    /**
     * 复制单个文件
     *
     * @param srcFileName
     *            待复制的文件名
     * @param srcFileName
     *            目标文件名
     * @param overlay
     *            如果目标文件存在，是否覆盖
     * @return 如果复制成功返回true，否则返回false
     */
    public static boolean copyFile(String srcFileName, String destFileName,
                                   boolean overlay) {
        File srcFile = new File(srcFileName);

        // 判断源文件是否存在
        if (!srcFile.exists()) {
            MESSAGE = "源文件：" + srcFileName + "不存在！";
            throw new RuntimeException(MESSAGE);
            //return false;
        } else if (!srcFile.isFile()) {
            MESSAGE = "复制文件失败，源文件：" + srcFileName + "不是一个文件！";
            throw new RuntimeException(MESSAGE);
            // return false;
        }

        // 判断目标文件是否存在
        File destFile = new File(destFileName);
        if (destFile.exists()) {
            // 如果目标文件存在并允许覆盖
            if (overlay) {
                // 删除已经存在的目标文件，无论目标文件是目录还是单个文件
                new File(destFileName).delete();
            }
        } else {
            // 如果目标文件所在目录不存在，则创建目录
            if (!destFile.getParentFile().exists()) {
                // 目标文件所在目录不存在
                if (!destFile.getParentFile().mkdirs()) {
                    // 复制文件失败：创建目标文件所在目录失败
                    return false;
                }
            }
        }

        // 复制文件
        int byteread = 0; // 读取的字节数
        InputStream in = null;
        OutputStream out = null;

        try {
            in = new FileInputStream(srcFile);
            out = new FileOutputStream(destFile);
            byte[] buffer = new byte[1024];

            while ((byteread = in.read(buffer)) != -1) {
                out.write(buffer, 0, byteread);
            }
            return true;
        } catch (FileNotFoundException e) {
            return false;
        } catch (IOException e) {
            return false;
        } finally {
            try {
                if (out != null)
                    out.close();
                if (in != null)
                    in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 复制整个目录的内容
     *
     * @param srcDirName
     *            待复制目录的目录名
     * @param destDirName
     *            目标目录名
     * @param overlay
     *            如果目标目录存在，是否覆盖
     * @return 如果复制成功返回true，否则返回false
     */
    public static boolean copyDirectory(String srcDirName, String destDirName,
                                        boolean overlay) {
        // 判断源目录是否存在
        File srcDir = new File(srcDirName);
        if (!srcDir.exists()) {
            MESSAGE = "复制目录失败：源目录" + srcDirName + "不存在！";
            throw new RuntimeException(MESSAGE);
            //return false;
        } else if (!srcDir.isDirectory()) {
            MESSAGE = "复制目录失败：" + srcDirName + "不是目录！";
            throw new RuntimeException(MESSAGE);
            //return false;
        }

        // 如果目标目录名不是以文件分隔符结尾，则加上文件分隔符
        if (!destDirName.endsWith(File.separator)) {
            destDirName = destDirName + File.separator;
        }
        File destDir = new File(destDirName);
        // 如果目标文件夹存在
        if (destDir.exists()) {
            // 如果允许覆盖则删除已存在的目标目录
            if (overlay) {
                new File(destDirName).delete();
            } else {
                MESSAGE = "复制目录失败：目的目录" + destDirName + "已存在！";
                throw new RuntimeException(MESSAGE);
                //return false;
            }
        } else {
            // 创建目的目录
            System.out.println("目的目录不存在，准备创建。。。");
            if (!destDir.mkdirs()) {
                System.out.println("复制目录失败：创建目的目录失败！");
                return false;
            }
        }

        boolean flag = true;
        File[] files = srcDir.listFiles();
        for (int i = 0; i < files.length; i++) {
            // 复制文件
            if (files[i].isFile()) {
                flag = copyFile(files[i].getAbsolutePath(),
                        destDirName + files[i].getName(), overlay);
                if (!flag)
                    break;
            } else if (files[i].isDirectory()) {
                flag = copyDirectory(files[i].getAbsolutePath(),
                        destDirName + files[i].getName(), overlay);
                if (!flag)
                    break;
            }
        }
        if (!flag) {
            MESSAGE = "复制目录" + srcDirName + "至" + destDirName + "失败！";
            throw new RuntimeException(MESSAGE);
            //return false;
        } else {
            return true;
        }
    }

    // 按一组文件，按一定大小分组，比如20g左右的很多文件，分成1g左右的，20个文件


    //读取目录及目录下所有文件


    // 读取目录下所有文件,递归调用
    public static void readFileList(File dir,List<String> fileList){
        if(! dir.isDirectory()){
            return;
        }
        File[]  files=dir.listFiles();
        for (File file : files) {
            if(file.isDirectory()){
                readFileList(file,fileList);
            }else {
                StringBuilder sb=new StringBuilder();
                System.out.println(file.getParent());
                System.out.println(file.getName());
                sb.append(file.getParent()).append(",").append(file.getName());
                fileList.add(sb.toString());
            }
        }
    }




    // 获取磁盘根目录list
    public static List<File> 	getDiskRootFileList(){
        File[] roots = File.listRoots();
        for (int i = 0; i < roots.length; i++) {
            System.out.print(roots[i].getPath()+"; ");//磁盘路径
            System.out.print(roots[i].getTotalSpace()/1024/1024/1024+"; ");//磁盘总空间大小
            System.out.print(roots[i].getUsableSpace()/1024/1024/1024+"; ");//剩余磁盘空间大小
            System.out.println(roots[i].getFreeSpace()/1024/1024/1024);//剩余磁盘空间大小
        }


        return Arrays.asList(roots);
    }


    // 获取磁盘根目录list,不含网络硬盘硬盘
    public static List<File> getLocalDiskRootFileList(){
        File[] roots = File.listRoots();
        List<File> fileList=new ArrayList<>();
        FileSystemView sys = FileSystemView.getFileSystemView();
        for (int i = 0; i < roots.length; i++) {
            if(!sys.getSystemTypeDescription(roots[i]).equals("本地磁盘")){
                continue;
            }
            System.out.println(roots[i].getPath());// 磁盘路径
            fileList.add(roots[i]);
        }
        return  fileList;
    }

    // 获取磁盘根目录list,不含本地硬盘硬盘
    public static List<File> getNetDiskRootFileList(){
        File[] roots = File.listRoots();
        List<File> fileList=new ArrayList<>();
        FileSystemView sys = FileSystemView.getFileSystemView();
        for (int i = 0; i < roots.length; i++) {
            if(!sys.getSystemTypeDescription(roots[i]).equals("网络驱动器")){
                continue;
            }
            System.out.println(roots[i].getPath());// 磁盘路径
            fileList.add(roots[i]);
        }
        return  fileList;
    }


    public static List<File> getDirList(File file){
        if(!file.exists() || ! file.isDirectory()){
            return null;
        }
        List<File> fileList=new ArrayList<>();
        return fileList;

    }

    /*
     *  dir: 拟采集目录
     *  filePathList: 所有文件地址存放的List
     * */
    public static void getAllRecursionFilePath(File dir, List<String> filePathList){
        File[] files=dir.listFiles();
        for(int i=0;i<files.length;i++){
            if(files[i].isDirectory()){
                //System.out.println(files[i].getPath());
                //这里面用了递归的算法
                getAllRecursionFilePath(files[i],filePathList);

            } else {
                //System.out.println(files[i].getPath());
                filePathList.add(files[i].getAbsolutePath());
            }
        }
    }


    public static List<String> getFilteredFileListOfRecursionDir(File dir,List<String> keywordList){
        List<String> allFilePath=new ArrayList<>();
        List<String> resultFileList=new ArrayList<>();
        getAllRecursionFilePath(dir,allFilePath);
        for (String s : allFilePath) {
            String fileName=s.substring(s.lastIndexOf("\\"));
            for (String keyword : keywordList) {
                if(fileName.toLowerCase().contains(keyword.toLowerCase())){
                    resultFileList.add(s);
                }
            }
        }
        return resultFileList;
    }

    //获取 指定目录下，所有目录组成的List
    public static List<File> getAllDirPathList(File dir,List<File> dirList){
        if(!dir.exists() || ! dir.isDirectory()){
            return dirList;
        }
        File[] files=dir.listFiles();
        for (File file : files) {
            if(!file.isDirectory()){
                continue;
            }
            dirList.add(file);
            getAllDirPathList(file,dirList);
        }

        return dirList;
    }

    //获取 指定目录下，所有目录组成的List
    public static List<File> getAllDirPathListAccordingDepth(File dir,List<File> dirList,int depth){
        if(!dir.exists() || ! dir.isDirectory() || depth<=0){
            return dirList;
        }
        File[] files=dir.listFiles();
        for (File file : files) {
            if(!file.isDirectory()){
                continue;
            }
            dirList.add(file);
            getAllDirPathListAccordingDepth(file,dirList,depth-1);
        }

        return dirList;
    }

    /**
     * get filtered file list of the directory
     * @param directory  absolute path of the directory
     * @param keywordList
     * @return
     */
    public static List<String> getFilteredFileListOfDir(String directory,List<String> keywordList){
        List<String> list=new ArrayList();
        File dir=new File(directory);
        if(! dir.isDirectory()){
            throw new RuntimeException("parameter1 directory is not valid directory path.");
        }
        List<File> fileList=getFilesOfDir(dir);
        if(keywordList==null || keywordList.size()==0){
            for (File file : fileList) {
                list.add(file.getAbsolutePath());
            }
            return  list;
        }

        for (File file : fileList) {
            String fileName=getFileName(file.getAbsolutePath());
            //System.out.println(fileName);
            for (String s : keywordList) {
                //System.out.println(s);
                if(fileName.contains(s)){
                    list.add((file.getAbsolutePath()));
                }
            }
        }
        return list;
    }

    /**
     *
     * @param directory
     * @param csvFileAbsPath the csv file which contains the keywords. The csv file doesn't need a headline.
     * @param columnIndexOfKeyword begin with 0
     *
     *
     * @return
     */
    public static List<String> getFilteredFileListOfDir(String directory,String csvFileAbsPath ,int columnIndexOfKeyword,String charset) throws IOException  {
        if(charset==null){
            charset="UTF-8";
        }
        List<String> stringList= NslUtilCsv.readCsvFile(new File(csvFileAbsPath),charset);
        List<String> keywordList=new ArrayList<>();
        for (String s : stringList) {
            String[] array=s.split(",");
            String keyword=array[columnIndexOfKeyword];
            keywordList.add(keyword);
        }
        return getFilteredFileListOfDir(directory,keywordList);
    }

    public static List<String> getFilteredFileListOfDir(String directory,String csvFileAbsPath ,int columnIndexOfKeyword) throws IOException {
        return getFilteredFileListOfDir(directory,csvFileAbsPath,columnIndexOfKeyword,null);
    }

    public static void main(String[] args) {
        String srcDirName = "C:/test/test0/test1";
        String destDirName = "c:/ttt";
        //UtilFile.copyDirectory(srcDirName, destDirName, true);
        //getDiskRootFileList();
        //getLocalDiskRootFileList();

        //getAllFilePath(new File("Z:\\GS_Dir_Zdisk\\"));

        //List<File> fileList=getAllDirPathList(new File("Z:\\GS_Dir_Zdisk\\GS#Dir\\GS#i3#Work\\2021Unicom"),new ArrayList<File>());

        //for (File file : fileList) {
        //	System.out.println(file.getPath());
        //}

    }
}
